home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / file-tra / rdist-6.1 / rdist-6 / rdist-6.1.0-linuxpl2 / src / docmd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-16  |  24.4 KB  |  1,066 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char RCSid[] = 
  36. "$Id: docmd.c,v 6.83 1994/04/26 17:10:07 mcooper Exp $";
  37.  
  38. static char sccsid[] = "@(#)docmd.c    5.1 (Berkeley) 6/6/85";
  39.  
  40. static char copyright[] =
  41. "@(#) Copyright (c) 1983 Regents of the University of California.\n\
  42.  All rights reserved.\n";
  43. #endif /* not lint */
  44.  
  45. /*
  46.  * Functions for rdist that do command (cmd) related activities.
  47.  */
  48.  
  49. #include "defs.h"
  50. #include <sys/socket.h>
  51. #include <netdb.h>
  52.  
  53. struct subcmd           *subcmds;        /* list of sub-commands for 
  54.                            current cmd */
  55. struct namelist           *filelist;        /* list of source files */
  56. extern struct cmd      *cmds;            /* Initialized by yyparse() */
  57. time_t            lastmod;        /* Last modify time */
  58.  
  59. extern char         target[];
  60. extern char            *ptarget;
  61. extern int        activechildren;
  62. extern int        maxchildren;
  63. extern int        amchild;
  64. extern char           *path_rdistd;
  65.  
  66. static void cmptime();
  67.  
  68. /*
  69.  * Signal end of connection.
  70.  */
  71. static void closeconn()
  72. {
  73.     debugmsg(DM_CALL, "closeconn() called\n");
  74.  
  75.     if (rem_w >= 0) {
  76.         /* We don't care if the connection is still good or not */
  77.         signal(SIGPIPE, SIG_IGN);    
  78.  
  79.         (void) sendcmd(C_FERRMSG, NULL);
  80.         (void) close(rem_w);
  81.         (void) close(rem_r); /* This can't hurt */
  82.         rem_w = -1;
  83.         rem_r = -1;
  84.     }
  85. }
  86.  
  87. /*
  88.  * Notify the list of people the changes that were made.
  89.  * rhost == NULL if we are mailing a list of changes compared to at time
  90.  * stamp file.
  91.  */
  92. static void notify(rhost, to, lmod)
  93.     char *rhost;
  94.     register struct namelist *to;
  95.     time_t lmod;
  96. {
  97.     register int fd, len;
  98.     FILE *pf, *popen();
  99.     struct stat stb;
  100.     static char buf[BUFSIZ];
  101.     char *file;
  102.  
  103.     if (IS_ON(options, DO_VERIFY) || to == NULL)
  104.         return;
  105.  
  106.     if ((file = getnotifyfile()) == NULL)
  107.         return;
  108.  
  109.     if (!IS_ON(options, DO_QUIET)) {
  110.         message(MT_INFO, "notify %s%s %s", 
  111.             (rhost) ? "@" : "",
  112.             (rhost) ? rhost : "", getnlstr(to));
  113.     }
  114.  
  115.     if (nflag)
  116.         return;
  117.  
  118.     debugmsg(DM_MISC, "notify() temp file = '%s'", file);
  119.  
  120.     if ((fd = open(file, O_RDONLY)) < 0) {
  121.         error("%s: open for reading failed: %s", file, SYSERR);
  122.         return;
  123.     }
  124.     if (fstat(fd, &stb) < 0) {
  125.         error("%s: fstat failed: %s", file, SYSERR);
  126.         (void) close(fd);
  127.         return;
  128.     }
  129.     if (stb.st_size == 0) {
  130.         (void) close(fd);
  131.         return;
  132.     }
  133.     /*
  134.      * Create a pipe to mailling program.
  135.      * Set IFS to avoid possible security problem with users
  136.      * setting "IFS=/".
  137.      */
  138.     (void) sprintf(buf, "IFS=\" \t\"; export IFS; %s -oi -t", 
  139.                _PATH_SENDMAIL);
  140.     pf = popen(buf, "w");
  141.     if (pf == NULL) {
  142.         error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
  143.         (void) unlink(file);
  144.         (void) close(fd);
  145.         return;
  146.     }
  147.     /*
  148.      * Output the proper header information.
  149.      */
  150.     (void) fprintf(pf, "From: rdist (Remote distribution program)\n");
  151.     (void) fprintf(pf, "To:");
  152.     if (!any('@', to->n_name) && rhost != NULL)
  153.         (void) fprintf(pf, " %s@%s", to->n_name, rhost);
  154.     else
  155.         (void) fprintf(pf, " %s", to->n_name);
  156.     to = to->n_next;
  157.     while (to != NULL) {
  158.         if (!any('@', to->n_name) && rhost != NULL)
  159.             (void) fprintf(pf, ", %s@%s", to->n_name, rhost);
  160.         else
  161.             (void) fprintf(pf, ", %s", to->n_name);
  162.         to = to->n_next;
  163.     }
  164.     (void) putc('\n', pf);
  165.     if (rhost != NULL)
  166.         (void) fprintf(pf, 
  167.                  "Subject: files updated by rdist from %s to %s\n",
  168.                    host, rhost);
  169.     else
  170.         (void) fprintf(pf, "Subject: files updated after %s\n", 
  171.                    ctime(&lmod));
  172.     (void) putc('\n', pf);
  173.     (void) putc('\n', pf);
  174.  
  175.     while ((len = read(fd, buf, sizeof(buf))) > 0)
  176.         (void) fwrite(buf, 1, len, pf);
  177.  
  178.     (void) pclose(pf);
  179.     (void) close(fd);
  180.     (void) unlink(file);
  181. }
  182.  
  183. /* 
  184.  * XXX Hack for NFS.  If a hostname from the distfile
  185.  * ends with a '+', then the normal restriction of
  186.  * skipping files that are on an NFS filesystem is
  187.  * bypassed.  We always strip '+' to be consistent.
  188.  */
  189. static void checkcmd(cmd)
  190.     struct cmd *cmd;
  191. {
  192.     int l;
  193.  
  194.     if (!cmd || !(cmd->c_name)) {
  195.         debugmsg(DM_MISC, "checkcmd() NULL cmd parameter");
  196.         return;
  197.     }
  198.  
  199.     l = strlen(cmd->c_name);
  200.     if (l <= 0)
  201.         return;
  202.     if (cmd->c_name[l-1] == '+') {
  203.         cmd->c_flags |= CMD_NOCHKNFS;
  204.         cmd->c_name[l-1] = CNULL;
  205.     }
  206. }
  207.  
  208. /*
  209.  * Mark all other entries for this command (cmd)
  210.  * as assigned.
  211.  */
  212. extern void markassigned(cmd, cmdlist)
  213.     struct cmd *cmd;
  214.     struct cmd *cmdlist;
  215. {
  216.     register struct cmd *pcmd;
  217.     
  218.     for (pcmd = cmdlist; pcmd; pcmd = pcmd->c_next) {
  219.         checkcmd(pcmd);
  220.         if (pcmd->c_type == cmd->c_type &&
  221.             strcmp(pcmd->c_name, cmd->c_name)==0)
  222.             pcmd->c_flags |= CMD_ASSIGNED;
  223.     }
  224. }
  225.  
  226. /*
  227.  * Mark the command "cmd" as failed for all commands in list cmdlist.
  228.  */
  229. static void markfailed(cmd, cmdlist)
  230.     struct cmd *cmd;
  231.     struct cmd *cmdlist;
  232. {
  233.     register struct cmd *pc;
  234.  
  235.     if (!cmd) {
  236.         debugmsg(DM_MISC, "markfailed() NULL cmd parameter");
  237.         return;
  238.     }
  239.  
  240.     checkcmd(cmd);
  241.     cmd->c_flags |= CMD_CONNFAILED;
  242.     for (pc = cmdlist; pc; pc = pc->c_next) {
  243.         checkcmd(pc);
  244.         if (pc->c_type == cmd->c_type &&
  245.             strcmp(pc->c_name, cmd->c_name)==0)
  246.             pc->c_flags |= CMD_CONNFAILED;
  247.     }
  248. }
  249.  
  250. static int remotecmd(rhost, luser, ruser, cmd)
  251.     char *rhost;
  252.     char *luser, *ruser;
  253.     char *cmd;
  254. {
  255.     int desc;
  256. #if    defined(DIRECT_RCMD)
  257.     static int port = -1;
  258. #endif    /* DIRECT_RCMD */
  259.  
  260.     debugmsg(DM_MISC, "local user = %s remote user = %s\n", luser, ruser);
  261.     debugmsg(DM_MISC, "Remote command = '%s'\n", cmd);
  262.  
  263.     (void) fflush(stdout);
  264.     (void) fflush(stderr);
  265.     (void) signal(SIGALRM, sighandler);
  266.     (void) alarm(RTIMEOUT);
  267.  
  268. #if    defined(DIRECT_RCMD)
  269.     (void) signal(SIGPIPE, sighandler);
  270.  
  271.     if (port < 0) {
  272.         struct servent *sp;
  273.         
  274.         if ((sp = getservbyname("shell", "tcp")) == NULL)
  275.                 fatalerr("shell/tcp: unknown service");
  276.         port = sp->s_port;
  277.     }
  278.  
  279.     if (becomeroot() != 0)
  280.         exit(1);
  281.     desc = rcmd(&rhost, port, luser, ruser, cmd, 0);
  282.     if (becomeuser() != 0)
  283.         exit(1);
  284. #else    /* !DIRECT_RCMD */
  285.     debugmsg(DM_MISC, "Remote shell command = '%s'\n", path_remsh);
  286.     (void) signal(SIGPIPE, SIG_IGN);
  287.     desc = rshrcmd(&rhost, -1, luser, ruser, cmd, 0);
  288.     if (desc > 0)
  289.         (void) signal(SIGPIPE, sighandler);
  290. #endif    /* DIRECT_RCMD */
  291.  
  292.     (void) alarm(0);
  293.  
  294.     return(desc);
  295. }
  296.  
  297. /*
  298.  * Create a connection to the rdist server on the machine rhost.
  299.  * Return 0 if the connection fails or 1 if it succeeds.
  300.  */
  301. static int makeconn(rhost)
  302.     char *rhost;
  303. {
  304.     register char *ruser, *cp;
  305.     static char *cur_host = NULL;
  306.     extern char *locuser;
  307.     extern long min_freefiles, min_freespace;
  308.     extern char *remotemsglist;
  309.     char tuser[BUFSIZ], buf[BUFSIZ];
  310.     u_char respbuff[BUFSIZ];
  311.     int n;
  312.  
  313.     debugmsg(DM_CALL, "makeconn(%s)", rhost);
  314.  
  315.     /*
  316.      * See if we're already connected to this host
  317.      */
  318.     if (cur_host != NULL && rem_w >= 0) {
  319.         if (strcmp(cur_host, rhost) == 0)
  320.             return(1);
  321.         closeconn();
  322.     }
  323.  
  324.     /*
  325.      * Determine remote user and current host names
  326.      */
  327.     cur_host = rhost;
  328.     cp = strchr(rhost, '@');
  329.  
  330.     if (cp != NULL) {
  331.         char c = *cp;
  332.  
  333.         *cp = CNULL;
  334.         (void) strncpy((char *)tuser, rhost, sizeof(tuser)-1);
  335.         *cp = c;
  336.         rhost = cp + 1;
  337.         ruser = tuser;
  338.         if (*ruser == CNULL)
  339.             ruser = locuser;
  340.         else if (!okname(ruser))
  341.             return(0);
  342.     } else
  343.         ruser = locuser;
  344.  
  345.     if (!IS_ON(options, DO_QUIET))
  346.         message(MT_VERBOSE, "updating host %s", rhost);
  347.  
  348.     (void) sprintf(buf, "%.*s -S", sizeof(buf)-5, path_rdistd);
  349.         
  350.     if ((rem_r = rem_w = remotecmd(rhost, locuser, ruser, buf)) < 0)
  351.         return(0);
  352.  
  353.     /*
  354.      * First thing received should be S_VERSION
  355.      */
  356.     n = remline(respbuff, sizeof(respbuff), TRUE);
  357.     if (n <= 0 || respbuff[0] != S_VERSION) {
  358.         error("Unexpected input from server: \"%s\".", respbuff);
  359.         closeconn();
  360.         return(0);
  361.     }
  362.  
  363.     /*
  364.      * For future compatibility we check to see if the server
  365.      * sent it's version number to us.  If it did, we use it,
  366.      * otherwise, we send our version number to the server and let
  367.      * it decide if it can handle our protocol version.
  368.      */
  369.     if (respbuff[1] == CNULL) {
  370.         /*
  371.          * The server wants us to send it our version number
  372.          */
  373.         (void) sendcmd(S_VERSION, "%d", VERSION);
  374.         if (response() < 0) 
  375.             return(0);
  376.     } else {
  377.         /*
  378.          * The server sent it's version number to us
  379.          */
  380.         proto_version = atoi(&respbuff[1]);
  381.         if (proto_version != VERSION) {
  382.             fatalerr(
  383.           "Server version (%d) is not the same as local version (%d).",
  384.                   proto_version, VERSION);
  385.             return(0);
  386.         }
  387.     }
  388.  
  389.     /*
  390.      * Send config commands
  391.      */
  392.     if (host[0]) {
  393.         (void) sendcmd(C_SETCONFIG, "%c%s", SC_HOSTNAME, host);
  394.         if (response() < 0)
  395.             return(0);
  396.     }
  397.     if (min_freespace) {
  398.         (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREESPACE, 
  399.                    min_freespace);
  400.         if (response() < 0)
  401.             return(0);
  402.     }
  403.     if (min_freefiles) {
  404.         (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREEFILES, 
  405.                    min_freefiles);
  406.         if (response() < 0)
  407.             return(0);
  408.     }
  409.     if (remotemsglist) {
  410.         (void) sendcmd(C_SETCONFIG, "%c%s", SC_LOGGING, remotemsglist);
  411.         if (response() < 0)
  412.             return(0);
  413.     }
  414.  
  415.     return(1);
  416. }
  417.  
  418. /*
  419.  * Process commands for sending files to other machines.
  420.  */
  421. static void doarrow(cmd, filev)
  422.     struct cmd *cmd;
  423.     char **filev;
  424. {
  425.     register struct namelist *f;
  426.     register struct subcmd *sc;
  427.     register char **cpp;
  428.     int n, ddir, destdir, opts = options;
  429.     struct namelist *files;
  430.     struct subcmd *sbcmds;
  431.     char *rhost;
  432.     int didupdate = 0;
  433.  
  434.     if (!cmd) {
  435.         debugmsg(DM_MISC, "doarrow() NULL cmd parameter");
  436.         return;
  437.     }
  438.  
  439.     files = cmd->c_files;
  440.     sbcmds = cmd->c_cmds;
  441.     rhost = cmd->c_name;
  442.  
  443.     if (files == NULL) {
  444.         error("No files to be updated on %s for target \"%s\"", 
  445.               rhost, cmd->c_label);
  446.         return;
  447.     }
  448.  
  449.     debugmsg(DM_CALL, "doarrow(%x, %s, %x) start", 
  450.          files, A(rhost), sbcmds);
  451.  
  452.     if (nflag)
  453.         (void) printf("updating host %s\n", rhost);
  454.     else {
  455.         if (cmd->c_flags & CMD_CONNFAILED) {
  456.             debugmsg(DM_MISC,
  457.                  "makeconn %s failed before; skipping\n",
  458.                  rhost);
  459.             return;
  460.         }
  461.  
  462.         if (setjmp(finish_jmpbuf)) {
  463.             debugmsg(DM_MISC, "setjmp to finish_jmpbuf");
  464.             markfailed(cmd, cmds);
  465.             return;
  466.         }
  467.  
  468.         if (!makeconn(rhost)) {
  469.             markfailed(cmd, cmds);
  470.             return;
  471.         }
  472.     }
  473.  
  474.     subcmds = sbcmds;
  475.     filelist = files;
  476.  
  477.     n = 0;
  478.     for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
  479.         if (sc->sc_type != INSTALL)
  480.             continue;
  481.         n++;
  482.     /*
  483.      * destination is a directory if one of the following is true:
  484.      * a) more than one name specified on left side of -> directive
  485.      * b) basename of destination in "install" directive is "."
  486.      *    (e.g. install /tmp/.;)
  487.      * c) name on left side of -> directive is a directory on local system.
  488.       *
  489.       * We need 2 destdir flags (destdir and ddir) because single directory
  490.       * source is handled differently.  In this case, ddir is 0 (which
  491.       * tells install() not to send DIRTARGET directive to remote rdistd)
  492.       * and destdir is 1 (which tells remfilename() how to build the FILE
  493.       * variables correctly).  In every other case, destdir and ddir will
  494.       * have the same value.
  495.      */
  496.       ddir = files->n_next != NULL;    /* destination is a directory */
  497.     if (!ddir) {
  498.         struct stat s;
  499.          int isadir = 0;
  500.  
  501.         if (lstat(files->n_name, &s) == 0)
  502.              isadir = S_ISDIR(s.st_mode);
  503.          if (!isadir && sc->sc_name && *sc->sc_name)
  504.              ddir = !strcmp(basename(sc->sc_name),".");
  505.          destdir = isadir | ddir;
  506.      } else
  507.          destdir = ddir;
  508.  
  509.     debugmsg(DM_MISC,
  510.          "Debug files->n_next= %d, destdir=%d, ddir=%d",
  511.          files->n_next, destdir, ddir);
  512.  
  513.     if (!sc->sc_name || !*sc->sc_name) {
  514.         destdir = 0;
  515.         ddir = 0;
  516.     }
  517.  
  518.     debugmsg(DM_MISC,
  519.          "Debug sc->sc_name=%x, destdir=%d, ddir=%d",
  520.          sc->sc_name, destdir, ddir);
  521.  
  522.     for (f = files; f != NULL; f = f->n_next) {
  523.         if (filev) {
  524.             for (cpp = filev; *cpp; cpp++)
  525.                 if (strcmp(f->n_name, *cpp) == 0)
  526.                     goto found;
  527.             continue;
  528.         }
  529.     found:
  530.         if (install(f->n_name, sc->sc_name, ddir, destdir,
  531.                 sc->sc_options) > 0)
  532.             ++didupdate;
  533.         opts = sc->sc_options;
  534.     }
  535.  
  536.     } /* end loop for each INSTALL command */
  537.  
  538.     /* if no INSTALL commands present, do default install */
  539.     if (!n) {
  540.         for (f = files; f != NULL; f = f->n_next) {
  541.             if (filev) {
  542.                 for (cpp = filev; *cpp; cpp++)
  543.                     if (strcmp(f->n_name, *cpp) == 0)
  544.                         goto found2;
  545.                 continue;
  546.             }
  547.         found2:
  548.             /* ddir & destdir set to zero for default install */
  549.             if (install(f->n_name, NULL, 0, 0, options) > 0)
  550.                 ++didupdate;
  551.         }
  552.     }
  553.  
  554. done:
  555.     /*
  556.      * Run any commands for the entire cmd
  557.      */
  558.     if (didupdate > 0) {
  559.         runcmdspecial(cmd, filev, opts);
  560.         didupdate = 0;
  561.     }
  562.  
  563.     if (!nflag)
  564.         (void) signal(SIGPIPE, cleanup);
  565.  
  566.     for (sc = sbcmds; sc != NULL; sc = sc->sc_next)
  567.         if (sc->sc_type == NOTIFY)
  568.             notify(rhost, sc->sc_args, (time_t) 0);
  569.  
  570.     if (!nflag) {
  571.         register struct linkbuf *nextl, *l;
  572.  
  573.         for (l = ihead; l != NULL; free((char *)l), l = nextl) {
  574.             nextl = l->nextp;
  575.             if (contimedout || IS_ON(opts, DO_IGNLNKS) || 
  576.                 l->count == 0)
  577.                 continue;
  578.             message(MT_WARNING, "%s: Warning: %d %s link%s",
  579.                 l->pathname, abs(l->count),    
  580.                 (l->count > 0) ? "missing" : "extra",
  581.                 (l->count == 1) ? "" : "s");
  582.         }
  583.         ihead = NULL;
  584.     }
  585. }
  586.  
  587. okname(name)
  588.     register char *name;
  589. {
  590.     register char *cp = name;
  591.     register int c, isbad;
  592.  
  593.     for (isbad = FALSE; *cp && !isbad; ++cp) {
  594.         c = *cp;
  595.         if (c & 0200)
  596.             isbad = TRUE;
  597.         if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
  598.             isbad = TRUE;
  599.     }
  600.  
  601.     if (isbad) {
  602.         error("Invalid user name \"%s\"\n", name);
  603.         return(0);
  604.     }
  605.     return(1);
  606. }
  607.  
  608. static void rcmptime(st, sbcmds, env)
  609.     struct stat *st;
  610.     struct subcmd *sbcmds;
  611.     char **env;
  612. {
  613.     register DIR *d;
  614.     register DIRENTRY *dp;
  615.     register char *cp;
  616.     char *optarget;
  617.     int len;
  618.  
  619.     debugmsg(DM_CALL, "rcmptime(%x) start", st);
  620.  
  621.     if ((d = opendir((char *) target)) == NULL) {
  622.         error("%s: open directory failed: %s", target, SYSERR);
  623.         return;
  624.     }
  625.     optarget = ptarget;
  626.     len = ptarget - target;
  627.     while (dp = readdir(d)) {
  628.         if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
  629.             continue;
  630.         if (len + 1 + (int)strlen(dp->d_name) >= BUFSIZ - 1) {
  631.             error("%s/%s: Name too long\n", target, dp->d_name);
  632.             continue;
  633.         }
  634.         ptarget = optarget;
  635.         *ptarget++ = '/';
  636.         cp = dp->d_name;
  637.         while (*ptarget++ = *cp++)
  638.             ;
  639.         ptarget--;
  640.         cmptime(target, sbcmds, env);
  641.     }
  642.     (void) closedir((DIR *) d);
  643.     ptarget = optarget;
  644.     *ptarget = '\0';
  645. }
  646.  
  647. /*
  648.  * Compare the mtime of file to the list of time stamps.
  649.  */
  650. static void cmptime(name, sbcmds, env)
  651.     char *name;
  652.     struct subcmd *sbcmds;
  653.     char **env;
  654. {
  655.     struct subcmd *sc;
  656.     struct stat stb;
  657.     int inlist();
  658.  
  659.     debugmsg(DM_CALL, "cmptime(%s)", name);
  660.  
  661.     if (except(name))
  662.         return;
  663.  
  664.     if (nflag) {
  665.         (void) printf("comparing dates: %s\n", name);
  666.         return;
  667.     }
  668.  
  669.     /*
  670.      * first time cmptime() is called?
  671.      */
  672.     if (ptarget == NULL) {
  673.         if (exptilde(target, name) == NULL)
  674.             return;
  675.         ptarget = name = target;
  676.         while (*ptarget)
  677.             ptarget++;
  678.     }
  679.     if (access(name, R_OK) < 0 || stat(name, &stb) < 0) {
  680.         error("%s: cannot access file: %s", name, SYSERR);
  681.         return;
  682.     }
  683.  
  684.     if (S_ISDIR(stb.st_mode)) {
  685.         rcmptime(&stb, sbcmds, env);
  686.         return;
  687.     } else if (!S_ISREG(stb.st_mode)) {
  688.         error("%s: not a plain file", name);
  689.         return;
  690.     }
  691.  
  692.     if (stb.st_mtime > lastmod) {
  693.         message(MT_INFO, "%s: file is newer", name);
  694.         for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
  695.             char buf[BUFSIZ];
  696.             if (sc->sc_type != SPECIAL)
  697.                 continue;
  698.             if (sc->sc_args != NULL && !inlist(sc->sc_args, name))
  699.                 continue;
  700.             (void) sprintf(buf, "%s=%s;%s", 
  701.                        E_LOCFILE, name, sc->sc_name);
  702.             message(MT_CHANGE, "special \"%s\"", buf);
  703.             if (*env) {
  704.                 int len = strlen(*env);
  705.                 *env = (char *) xrealloc(*env, len +
  706.                              strlen(name) + 2);
  707.                 *env[len] = CNULL;
  708.                 (void) strcat(*env, name);
  709.                 (void) strcat(*env, ":");
  710.             }
  711.             if (IS_ON(options, DO_VERIFY))
  712.                 continue;
  713.  
  714.             runcommand(buf);
  715.         }
  716.     }
  717. }
  718.  
  719. /*
  720.  * Process commands for comparing files to time stamp files.
  721.  */
  722. static void dodcolon(cmd, filev)
  723.     struct cmd *cmd;
  724.     char **filev;
  725. {
  726.     register struct subcmd *sc;
  727.     register struct namelist *f;
  728.     register char *cp, **cpp;
  729.     struct stat stb;
  730.     struct namelist *files = cmd->c_files;
  731.     struct subcmd *sbcmds = cmd->c_cmds;
  732.     char *env, *stamp = cmd->c_name;
  733.  
  734.     debugmsg(DM_CALL, "dodcolon()");
  735.  
  736.     if (files == NULL) {
  737.         error("No files to be updated for target \"%s\"", 
  738.               cmd->c_label);
  739.         return;
  740.     }
  741.     if (stat(stamp, &stb) < 0) {
  742.         error("%s: stat failed: %s", stamp, SYSERR);
  743.         return;
  744.     }
  745.  
  746.     debugmsg(DM_MISC, "%s: mtime %d\n", stamp, stb.st_mtime);
  747.  
  748.     env = NULL;
  749.     for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
  750.         if (sc->sc_type == CMDSPECIAL) {
  751.             env = (char *) xmalloc(sizeof(E_FILES) + 3);
  752.             (void) sprintf(env, "%s='", E_FILES);
  753.             break;
  754.         }
  755.     }
  756.  
  757.     subcmds = sbcmds;
  758.     filelist = files;
  759.  
  760.     lastmod = stb.st_mtime;
  761.     if (!nflag && !IS_ON(options, DO_VERIFY))
  762.         /*
  763.          * Set atime and mtime to current time
  764.          */
  765.         (void) setfiletime(stamp, (time_t) 0, (time_t) 0);
  766.  
  767.     for (f = files; f != NULL; f = f->n_next) {
  768.         if (filev) {
  769.             for (cpp = filev; *cpp; cpp++)
  770.                 if (strcmp(f->n_name, *cpp) == 0)
  771.                     goto found;
  772.             continue;
  773.         }
  774.     found:
  775.         ptarget = NULL;
  776.         cmptime(f->n_name, sbcmds, &env);
  777.     }
  778.  
  779.     for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
  780.         if (sc->sc_type == NOTIFY)
  781.             notify((char *)NULL, sc->sc_args, (time_t)lastmod);
  782.         else if (sc->sc_type == CMDSPECIAL && env) {
  783.             char *p;
  784.             int len = strlen(env);
  785.  
  786.             env = xrealloc(env, 
  787.                        len + strlen(sc->sc_name) + 2);
  788.             env[len] = CNULL;
  789.             if (*(p = &env[len - 1]) == ':')
  790.                 *p = CNULL;
  791.             (void) strcat(env, "';");
  792.             (void) strcat(env, sc->sc_name);
  793.             message(MT_CHANGE, "cmdspecial \"%s\"", env);
  794.             if (!nflag && IS_OFF(options, DO_VERIFY))
  795.                 runcommand(env);
  796.             (void) free(env);
  797.             env = NULL;    /* so cmdspecial is only called once */
  798.         }
  799.     }
  800.     if (!nflag && !IS_ON(options, DO_VERIFY) && (cp = getnotifyfile()))
  801.         (void) unlink(cp);
  802. }
  803.  
  804. /*
  805.  * Return TRUE if file is in the exception list.
  806.  */
  807. extern int except(file)
  808.     char *file;
  809. {
  810.     register struct    subcmd *sc;
  811.     register struct    namelist *nl;
  812.  
  813.     debugmsg(DM_CALL, "except(%s)", file);
  814.  
  815.     for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
  816.         if (sc->sc_type == EXCEPT) {
  817.             for (nl = sc->sc_args; nl != NULL; nl = nl->n_next)
  818.                 if (strcmp(file, nl->n_name) == 0)
  819.                     return(1);
  820.               continue;
  821.         }
  822.         if (sc->sc_type == PATTERN) {
  823.             for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
  824.                 char *cp, *re_comp();
  825.  
  826.                 if ((cp = re_comp(nl->n_name)) != NULL) {
  827.                     error("Regex error \"%s\" for \"%s\".",
  828.                           cp, nl->n_name);
  829.                     return(0);
  830.                 }
  831.                 if (re_exec(file) > 0)
  832.                       return(1);
  833.             }
  834.         }
  835.     }
  836.     return(0);
  837. }
  838.  
  839. /*
  840.  * Do a specific command for a specific host
  841.  */
  842. static void docmdhost(cmd, filev)
  843.     struct cmd *cmd;
  844.     char **filev;
  845. {
  846.     checkcmd(cmd);
  847.  
  848.     /*
  849.      * If we're multi-threaded and we're the parent, spawn a 
  850.      * new child process.
  851.      */
  852.     if (do_fork && !amchild) {
  853.         int pid;
  854.  
  855.         /*
  856.          * If we're at maxchildren, wait for number of active
  857.          * children to fall below max number of children.
  858.          */
  859.         while (activechildren >= maxchildren)
  860.             waitup();
  861.  
  862.         pid = spawn(cmd, cmds);
  863.         if (pid == 0)
  864.             /* Child */
  865.             amchild = 1;
  866.         else
  867.             /* Parent */
  868.             return;
  869.     }
  870.  
  871.     /*
  872.      * Disable NFS checks
  873.      */
  874.     if (cmd->c_flags & CMD_NOCHKNFS)
  875.         FLAG_OFF(options, DO_CHKNFS);
  876.  
  877.     if (!nflag) {
  878.         currenthost = (cmd->c_name) ? cmd->c_name : "<unknown>";
  879. #if    defined(SETARGS)
  880.         setproctitle("update %s", currenthost);
  881. #endif     /* SETARGS */
  882.     }
  883.  
  884.     switch (cmd->c_type) {
  885.     case ARROW:
  886.         doarrow(cmd, filev);
  887.         break;
  888.     case DCOLON:
  889.         dodcolon(cmd, filev);
  890.         break;
  891.     default:
  892.         fatalerr("illegal command type %d", cmd->c_type);
  893.     }
  894. }
  895.  
  896. /*
  897.  * Do a specific command (cmd)
  898.  */
  899. static void docmd(cmd, argc, argv)
  900.     struct cmd *cmd;
  901.     int argc;
  902.     char **argv;
  903. {
  904.     register struct namelist *f;
  905.     register int i;
  906.  
  907.     if (argc) {
  908.         for (i = 0; i < argc; i++) {
  909.             if (cmd->c_label != NULL &&
  910.                 strcmp(cmd->c_label, argv[i]) == 0) {
  911.                 docmdhost(cmd, (char **) NULL);
  912.                 return;
  913.             }
  914.             for (f = cmd->c_files; f != NULL; f = f->n_next)
  915.                 if (strcmp(f->n_name, argv[i]) == 0) {
  916.                     docmdhost(cmd, &argv[i]);
  917.                     return;
  918.                 }
  919.         }
  920.     } else
  921.         docmdhost(cmd, (char **) NULL);
  922. }
  923.  
  924. /*
  925.  *
  926.  * Multiple hosts are updated at once via a "ring" of at most
  927.  * maxchildren rdist processes.  The parent rdist fork()'s a child
  928.  * for a given host.  That child will update the given target files
  929.  * and then continue scanning through the remaining targets looking
  930.  * for more work for a given host.  Meanwhile, the parent gets the
  931.  * next target command and makes sure that it hasn't encountered
  932.  * that host yet since the children are responsible for everything
  933.  * for that host.  If no children have done this host, then check
  934.  * to see if the number of active proc's is less than maxchildren.
  935.  * If so, then spawn a new child for that host.  Otherwise, wait
  936.  * for a child to finish.
  937.  *
  938.  */
  939.  
  940. /*
  941.  * Do the commands in cmds (initialized by yyparse).
  942.  */
  943. extern void docmds(hostlist, argc, argv)
  944.     struct namelist *hostlist;
  945.     int argc;
  946.     char **argv;
  947. {
  948.     register struct cmd *c;
  949.     register char *cp;
  950.     register int i;
  951.  
  952.     (void) signal(SIGHUP, sighandler);
  953.     (void) signal(SIGINT, sighandler);
  954.     (void) signal(SIGQUIT, sighandler);
  955.     (void) signal(SIGTERM, sighandler);
  956.  
  957.     if (!nflag)
  958.         mysetlinebuf(stdout);    /* Make output (mostly) clean */
  959.  
  960. #if    defined(USE_STATDB)
  961.     if (!nflag && (dostatdb || juststatdb)) {
  962.         extern long reccount;
  963.         message(MT_INFO, "Making stat database [%s] ... \n", 
  964.                    gettimestr());
  965.         if (mkstatdb() < 0)
  966.             error("Warning: Make stat database failed.");
  967.         message(MT_INFO,
  968.                   "Stat database created: %d files stored [%s].\n",
  969.                    reccount, gettimestr());
  970.         if (juststatdb)
  971.             return;
  972.     }
  973. #endif    /* USE_STATDB */
  974.  
  975.     /*
  976.      * Print errors for any command line targets we didn't find.
  977.      * If any errors are found, return to main() which will then exit.
  978.      */
  979.     for (i = 0; i < argc; i++) {
  980.         int found;
  981.  
  982.         for (found = FALSE, c = cmds; c != NULL; c = c->c_next) {
  983.             if (c->c_label && argv[i] && 
  984.                 strcmp(c->c_label, argv[i]) == 0) {
  985.                 found = TRUE;
  986.                 break;
  987.             }
  988.         }
  989.         if (!found)
  990.             error("Label \"%s\" is not defined in the distfile.", 
  991.                   argv[i]);
  992.     }
  993.     if (nerrs)
  994.         return;
  995.  
  996.     /*
  997.      * Main command loop.  Loop through all the commands.
  998.      */
  999.     for (c = cmds; c != NULL; c = c->c_next) {
  1000.         checkcmd(c);
  1001.         if (do_fork) {
  1002.             /*
  1003.              * Let the children take care of their assigned host
  1004.              */
  1005.             if (amchild) {
  1006.                 if (strcmp(c->c_name, currenthost) != 0)
  1007.                     continue;
  1008.             } else if (c->c_flags & CMD_ASSIGNED) {
  1009.                 /* This cmd has been previously assigned */
  1010.                 debugmsg(DM_MISC, "prev assigned: %s\n",
  1011.                      c->c_name);
  1012.                 continue;
  1013.             }
  1014.         }
  1015.  
  1016.         if (hostlist) {
  1017.             /* Do specific hosts as specified on command line */
  1018.             register struct namelist *nlptr;
  1019.  
  1020.             for (nlptr = hostlist; nlptr; nlptr = nlptr->n_next)
  1021.                 /*
  1022.                  * Try an exact match and then a match
  1023.                  * without '@' (if present).
  1024.                  */
  1025.                 if ((strcmp(c->c_name, nlptr->n_name) == 0) ||
  1026.                     ((cp = strchr(c->c_name, '@')) &&
  1027.                      strcmp(++cp, nlptr->n_name) == 0))
  1028.                     docmd(c, argc, argv);
  1029.             continue;
  1030.         } else
  1031.             /* Do all of the command */
  1032.             docmd(c, argc, argv);
  1033.     }
  1034.  
  1035.     if (do_fork) {
  1036.         /*
  1037.          * We're multi-threaded, so do appropriate shutdown
  1038.          * actions based on whether we're the parent or a child.
  1039.          */
  1040.         if (amchild) {
  1041.             if (!IS_ON(options, DO_QUIET))
  1042.                 message(MT_VERBOSE, "updating of %s finished", 
  1043.                     currenthost);
  1044.             closeconn();
  1045.             cleanup();
  1046.             exit(nerrs);
  1047.         }
  1048.  
  1049.         /*
  1050.          * Wait for all remaining active children to finish
  1051.          */
  1052.         while (activechildren > 0) {
  1053.             debugmsg(DM_MISC, 
  1054.                  "Waiting for %d children to finish.\n",
  1055.                  activechildren);
  1056.             waitup();
  1057.         }
  1058.     } else if (!nflag) {
  1059.         /*
  1060.          * We're single-threaded so close down current connection
  1061.          */
  1062.         closeconn();
  1063.         cleanup();
  1064.     }
  1065. }
  1066.